<?php
/**
 * 影流摄像头 - 夸父自研平台
 */

namespace App\Kuafu\Shop\Service\CameraLibrary;

use App\Kuafu\Shop\Enum\CodeEnum;
use App\Library\FeiShu\SendFs;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Psr7\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use KuafuComposer\ApiTime\Service\ApiTimeService;
use Symfony\Component\HttpFoundation\Request as RequestAlias;

class YlCamera extends CameraBase implements CameraInterface
{
    private string $host;

    private string $accessKeyCacheKey = 'ylCameraAccessToken';

    public function init(): void
    {
        $this->host = config('camera.yl.host');
    }

    /**
     * 获取设备列表
     *
     * @return array
     */
    public function getDeviceList(): array
    {
        $uri = 'api/device/query/devices';

        $query = [
            'page'  => 1,
            'count' => 2000,
        ];

        return $this->formatReturn($this->curl($uri, RequestAlias::METHOD_GET, $query));
    }

    /**
     * 批量获取设备状态
     *
     * @param array $cameraList
     * @return array
     */
    public function getDeviceStatusBatch(array $cameraList): array
    {
        // 批量获取di
        $cameraListDn = array_column($cameraList, null, 'deviceName');
        $diList       = array_column($cameraList, 'productKey');
        $diList       = array_unique($diList);

        foreach ($diList as $item) {
            $channelStatus = $this->getDeviceChannelsStatus($item);

            // 获取状态
            $cnList = $this->getDeviceChannelsList($item);

            $cnList = $cnList['list'];

            foreach ($cnList as $cnOne) {
                if (empty($cameraListDn[$cnOne['channelId']])) {
                    continue;
                }
                if (empty($channelStatus)) {
                    $cameraListDn[$cnOne['channelId']]['status'] = 0;
                } else {
                    $cameraListDn[$cnOne['channelId']]['status'] = (int)$cnOne['status'];
                }
            }
        }
        return array_values($cameraListDn);
    }

    /**
     * 分页查询国标设备
     *
     * @param string $deviceId
     * @return array
     */
    public function getDeviceChannelsList(string $deviceId): array
    {
        $uri = 'api/device/query/devices/' . $deviceId . '/channels';

        $query = [
            'page'  => 1,
            'count' => 100,
        ];

        return $this->formatReturn($this->curl($uri, RequestAlias::METHOD_GET, $query));
    }

    /**
     * 同步设备通道
     *
     * @param string $deviceId
     * @return array
     */
    public function postDeviceChannelsListSync(string $deviceId): array
    {
        $uri = 'api/device/query/devices/' . $deviceId . '/sync';

        return $this->formatReturn($this->curl($uri, RequestAlias::METHOD_GET));
    }

    /**
     * 设备状态查询
     *
     * @param string $deviceId
     * @return array
     */
    public function getDeviceChannelsStatus(string $deviceId): array
    {
        $uri    = "/api/device/query/devices/{$deviceId}/status";
        $result = $this->curl($uri, RequestAlias::METHOD_GET, [], 60);

        if (is_string($result['data'])) {
            $result['data'] = [];
        }

        return $this->formatReturn($result);
    }

    /**
     * 同步设备通道状态查询
     *
     * @param string $deviceId
     * @return array
     */
    public function getDeviceChannelsListSyncStatus(string $deviceId): array
    {
        $uri = 'api/device/query/' . $deviceId . '/sync_status';

        return $this->formatReturn($this->curl($uri, RequestAlias::METHOD_GET));
    }

    /**
     * 更新设备通道
     *
     * @param string $deviceId
     * @param array $params
     * @return array
     */
    public function postUpdateDeviceChannel(string $deviceId, array $params = []): array
    {
        $uri = "api/device/query/channel/update/{$deviceId}";

        $params = http_build_query($params);

        $uri .= '?' . $params;
        return $this->formatReturn($this->curl($uri, RequestAlias::METHOD_POST));
    }

    /**
     * 直播流
     *
     * @param string $scheme
     * @param int $clarity
     * @return array|string[]
     */
    public function getLiveStreaming(
        string $scheme,
        int $clarity = CodeEnum::CLARITY_TYPE_FLUENCY
    ): array {
        $scheme    = self::switchScheme($scheme);
        $channelId = $this->cameraModel->deviceName;
        $deviceId  = $this->cameraModel->productKey;

        $uri = "api/play/start/{$deviceId}/{$channelId}";

        $result = $this->formatReturn($this->curl($uri, RequestAlias::METHOD_GET));

        $playUrl = $result[$scheme] ?? '';

        return [
            'url' => $playUrl
        ];
    }


    /**
     * 本地录像
     *
     * @param int $startAt
     * @param int $endAt
     * @return array
     */
    public function localVideoList(int $startAt, int $endAt): array
    {
        $channelId = $this->cameraModel->deviceName;
        $deviceId  = $this->cameraModel->productKey;

        $query = [
            'startTime' => date('Y-m-d H:i:s', $startAt),
            'endTime'   => date('Y-m-d H:i:s', $endAt)
        ];

        $uri = "api/gb_record/query/{$deviceId}/{$channelId}";

        $data = $this->formatReturn($this->curl($uri, RequestAlias::METHOD_GET, $query));

        $return = [];
        if (!empty($data['recordList'])) {
            foreach ($data['recordList'] as $datum) {
                $return [] = [
                    'beginTime' => strtotime($datum['startTime']) ?? 0,
                    'endTime'   => strtotime($datum['endTime']) ?? 0
                ];
            }
        }

        foreach ($return as &$r) {
            if ($r['beginTime'] < $startAt) {
                $r['beginTime'] = $startAt;
            }

            if ($r['endTime'] > $endAt) {
                $r['endTime'] = $endAt;
            }
        }
        return $return;
    }


    /**
     * 本地录像播放
     *
     * @param int $startAt
     * @param int $endAt
     * @param int $seekTime
     * @param string $scheme
     * @return array
     */
    public function backUrl(int $startAt, int $endAt, int $seekTime, string $scheme = 'flv'): array
    {
        $scheme    = self::switchScheme($scheme);
        $channelId = $this->cameraModel->deviceName;
        $deviceId  = $this->cameraModel->productKey;

        $query = [
            'startTime' => date('Y-m-d H:i:s', $startAt + $seekTime),
            'endTime'   => date('Y-m-d H:i:s', $endAt)
        ];

        $uri = "api/playback/start/{$deviceId}/{$channelId}";

        $result = $this->formatReturn($this->curl($uri, RequestAlias::METHOD_GET, $query));

        $playUrl = $result[$scheme] ?? '';

        return [
            'url'         => $playUrl,
            'domain_list' => ''
        ];
    }


    /**
     * 心跳接口
     *
     * @param string $type
     * @param string $playType
     * @param int $rate
     * @return array
     */
    public function heartBeat(string $type = '1', string $playType = 'hls', int $rate = 1000): array
    {
        return [];
    }

    /**
     * 设备状态
     *
     * @return int
     */
    public function getDeviceStatus(): int
    {

    }

    /**
     * 根据门店id、小时等条件查询门店客流统计数据。
     *
     * @param string $date
     * @return array
     */
    public function getStatisticHourlySummary(string $date = ''): array
    {
        return [];
    }


    /**
     * 获取 accessToken
     *
     * @return string
     */
    private function getAccessToken(): string
    {
        $accessToken = '';
//        if (true || !$accessToken = Redis::get($this->accessKeyCacheKey)) {

        $uri      = 'api/user/login';
        $username = config('camera.yl.username');
        $password = config('camera.yl.password');
        $query    = [
            'username' => $username,
            'password' => md5($password),
        ];
        $result   = $this->curl($uri, RequestAlias::METHOD_GET, $query);

        if (isset($result['code']) && $result['code'] == 0) {
            $accessToken = $result['data']['accessToken'];
//                Redis::set($this->accessKeyCacheKey, $accessToken, 'ex', 864000);
        }
//        }

        return $accessToken;
    }

    /**
     * 返回数据
     *
     * @param string $uri
     * @param string $method
     * @param array $param
     * @param int $cacheTime
     * @return array|mixed
     */
    public function curl(string $uri = '', string $method = '', array $param = [], int $cacheTime = 0): array
    {
        $uri = ltrim($uri, '/');
        try {
            return Cache::remember($this->genCacheKey($uri, $param), getSwitchCacheTime($cacheTime),
                function () use ($uri, $method, $param, $cacheTime) {

                    $client = new Client(['http_errors' => false]);
                    $logUri = $requestUri = $this->host . $uri;

                    $header = [
                    ];

                    if ($uri != 'api/user/login') {
                        $header['access-token'] = $this->getAccessToken();
                    }

                    $startMicroTime = microtime(true);

                    $requestUri = strtoupper($method) == RequestAlias::METHOD_GET ? $requestUri . "?" . http_build_query($param) : $requestUri;

                    $request = new Request(
                        $method,
                        $requestUri,
                        $header,
                        null
                    );

                    Log::info('camera-yingliu-request:', [
                        'url'       => $requestUri,
                        'header'    => $header,
                        'cacheTime' => $cacheTime,
                    ]);

                    $option = [];
                    if ($method == RequestAlias::METHOD_POST) {
                        $option = [
                            'form_params' => $param
                        ];
                    }

                    $response = $client->send($request, $option);

                    try {
                        (new ApiTimeService('ylCamera'))->add($logUri, $startMicroTime);
                    } catch (\Exception $e) {
                        Log::warning($e);
                    }

                    $returnStr = $response->getBody()->getContents();

                    if (isJsonStr($returnStr)) {
                        return json_decode($returnStr, true);
                    } else {
                        return [];
                    }
                });
        } catch (RequestException|\Exception $exception) {
            return [];
        }
    }

    /**
     * 缓存 Key
     *
     * @param string $uri
     * @param array $params
     * @return string
     */
    private function genCacheKey(string $uri, array $params = []): string
    {
        return 'cache:camera:yl:' . $uri . ':' . md5($uri . implode(',', $params));
    }

    /**
     * 格式化输出
     *
     * @param string|array|null $data
     * @return array
     */
    private function formatReturn($data): array
    {
        if (empty($data)) {
            return [];
        }
        if ($data['code'] === 0) {
            return $data['data'] ?? $data;
        } else {
            return $data;
        }
    }


    public function logErr(string $title, array $result, \Exception $exception, array $requestParam = []): void
    {
        // TODO: Implement logErr() method.
    }

    public function switchClarity(int $clarity = CodeEnum::CLARITY_TYPE_FLUENCY): int
    {
        // TODO: Implement switchClarity() method.
    }


    /**
     * 夸父自研平台-协议转换
     * @param string $scheme
     * @return string
     */
    public function switchScheme(string $scheme): string
    {
        switch ($scheme) {
            case 'hls':
                $scheme = 'https_hls';
                break;
            case 'flv':
                $scheme = 'https_flv';
                break;
        }
        return $scheme;
    }
}
